/*
 * This file is part of SND.
 *
 * Sensor Network Deployer
 * Copyright (C)  Michael Morckos 2008 <mikey.morckos@gmail.com>
 * 
 * SND is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * SND is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with SND.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.util.LinkedList;

public class Deployer {
	
	private static final long serialVersionUID = 1L;
	
	private final int GEOMETRIC_RANDOM_GRAPH_N_R = 0;
	private final int GEOMETRIC_RANDOM_GRAPH_N_K = 1;
	private final int GEOMETRIC_RANDOM_GRAPH_N_P_R = 2;
	private final int BERNOULLI_RANDOM_GRAPH_N_P = 3;
	
	private final int MIN = 50;
	
	/* DEFINING WHICH GRAPH TO USE */
	private int mode;
	private LinkedList nodes;
	private LinkedList edges;
	
	private int num_of_nodes, T_R, height, width; 
	private float prob;
	private boolean reset;
	
	private Graph_Panel p;

	public Deployer() {
		
		reset = true;
		nodes = null;
		edges = null;
	}
	
	/* FOR DEPLOYING THE NODES */
	public void deployNodes(int T_R, int num_of_nodes, int width, int height, float prob, int mode, Graph_Panel p) {
		
		this.num_of_nodes = num_of_nodes;
		this.T_R = T_R;
		this.width = width + 50;
		this.height = height + 50;
		this.prob = prob;
		this.mode = mode;
		this.p = p;
		
		if(p.isBusy())
			return;
		
		nodes = new LinkedList();
		edges = new LinkedList();
		
		int sleep_nodes = 0, counter = 0;
		if(mode == 2)
			sleep_nodes = (int)((1-prob) * num_of_nodes);
		
		for(int i = 0; i < num_of_nodes; i++) {
			if(mode == 2 && counter < sleep_nodes) {
				if(Math.random() < 0.5) {
					counter++;
					nodes.addLast(new Node(i, 0, 0, T_R, false));
				}	
				else
					nodes.addLast(new Node(i, 0, 0, T_R, true));
			}
			else
				nodes.addLast(new Node(i, 0, 0, T_R, true));
		}	
		
		randomDistribution();
		
		p.drawArea(width, height);
		p.drawNodes(nodes);
		
		switch(mode) {
            case GEOMETRIC_RANDOM_GRAPH_N_R : GEOMETRIC_RANDOM_GRAPH_N_R(); break;
            case GEOMETRIC_RANDOM_GRAPH_N_K : GEOMETRIC_RANDOM_GRAPH_N_K(); break;
            case GEOMETRIC_RANDOM_GRAPH_N_P_R : GEOMETRIC_RANDOM_GRAPH_N_P_R(); break;
            case BERNOULLI_RANDOM_GRAPH_N_P: BERNOULLI_RANDOM_GRAPH_N_P(); break;
        }

        p.drawEdges(edges);
	}
	
	public void GEOMETRIC_RANDOM_GRAPH_N_R() {
		
		Node temp, temp2;
		int distance;
		
		System.out.println("GEOMETRIC_RANDOM_GRAPH_N_R");
		
		for(int i = 0; i < nodes.size(); i++) {
			temp = (Node)nodes.get(i);
			
			for(int j = 0; j < nodes.size(); j++) {
				temp2 = (Node)nodes.get(j);
				distance = distance(temp.getX(), temp.getY(), temp2.getX(), temp2.getY());
				
				if( distance<= T_R && distance != 0)
					edges.addLast(new Edge(temp.getX(), temp.getY(), temp2.getX(), temp2.getY()));
			}
		}
	}
	
	/* CONNECTS EACH NODES TO K NEIGHBOURS. K IS CALCULATED BASED ON NUMBER OF NODES */
    public void GEOMETRIC_RANDOM_GRAPH_N_K() {
		
    	Node temp, temp2;
		int distance;
		
		System.out.println("GEOMETRIC_RANDOM_GRAPH_N_K");
		
		int K_MIN = (int)(0.074 * Math.log10(num_of_nodes)); 
		int K_MAX = (int)(2.72 * Math.log10(num_of_nodes));
		
		for(int i = 0; i < nodes.size(); i++) {
			temp = (Node)nodes.get(i);
			
			for(int j = 0; j < nodes.size(); j++) {
				temp2 = (Node)nodes.get(j);
				
				if(temp2.getNEIGHBOURS() > K_MAX - 1)
					continue;
				
				distance = distance(temp.getX(), temp.getY(), temp2.getX(), temp2.getY());
				
				if( distance<= T_R && distance != 0) {
					edges.addLast(new Edge(temp.getX(), temp.getY(), temp2.getX(), temp2.getY()));
				    temp.addNEIGHBOUR(temp2);
				    temp2.addNEIGHBOUR(temp);
				}	
			}
		}	
	}
    
    public void GEOMETRIC_RANDOM_GRAPH_N_P_R() {
    	
    	Node temp, temp2;
		int distance;
		
		System.out.println("GEOMETRIC_RANDOM_GRAPH_N_P_R");
		
		for(int i = 0; i < nodes.size(); i++) {
			temp = (Node)nodes.get(i);
			
			if(!temp.getSTATUS())
				continue;
			
			for(int j = 0; j < nodes.size(); j++) {
				temp2 = (Node)nodes.get(j);
				
				if(!temp2.getSTATUS())
					continue;
				
				distance = distance(temp.getX(), temp.getY(), temp2.getX(), temp2.getY());
				
				if( distance<= T_R && distance != 0)
					edges.addLast(new Edge(temp.getX(), temp.getY(), temp2.getX(), temp2.getY()));
			}
		}
	}
    
    public void BERNOULLI_RANDOM_GRAPH_N_P() {
    	
    	Node temp, temp2;
		int distance;
		
		System.out.println("BERNOULLI_RANDOM_GRAPH_N_P");
		
		for(int i = 0; i < nodes.size(); i++) {
			temp = (Node)nodes.get(i);
			
			if (Math.random() < prob) {
				temp.setCONNECTED(false);
				continue;
			}

			for(int j = 0; j < nodes.size(); j++) {
				temp2 = (Node)nodes.get(j);
				distance = distance(temp.getX(), temp.getY(), temp2.getX(), temp2.getY());
				
				if(!temp2.getCONNECTED()) 
					continue;
				
				if( distance<= T_R && distance != 0)
					edges.addLast(new Edge(temp.getX(), temp.getY(), temp2.getX(), temp2.getY()));
			}
		}
    }
    
    /* RANDOM DISTRIBUTION OF NODES */
    public void randomDistribution() {
    	
    	LinkedList temp = nodes;
    	Node temp_node;
    	
    	int x_range, y_range;
    	
    	/* DEPLOYING THE NODES ON FOUR PARTS UNIFORMALLY*/
    	int div_nodes = nodes.size() / 4;
    	
    	int rand_x = 0, rand_y = 0;
    	
    	/* FIRST */
    	x_range = width / 2;
    	y_range = height / 2;
    	
    	for(int i = 0; i < div_nodes; i++) {
    		temp_node = (Node)nodes.pop();
    		
    		while(!(rand_x >= 50 && rand_x <= x_range && rand_y >= 50 && rand_y <= y_range)) {
    			rand_x = (int)(Math.random() * 1000);
    			rand_y = (int)(Math.random() * 1000);
    		}
    		
    		temp_node.setX(rand_x);
    		temp_node.setY(rand_y);
    		
    		rand_x = 0;
    		rand_y = 0;
    		
    		temp.addLast(temp_node);
    	}
    	
    	/* SECOND */
    	x_range = width / 2;
    	
        for(int i = 0; i < div_nodes; i++) {
    		temp_node = (Node)nodes.pop();
    		
    		while(!(rand_x >= x_range && rand_x <= width && rand_y >= 50 && rand_y <= y_range)) {
    			rand_x = (int)(Math.random() * 1000);
    			rand_y = (int)(Math.random() * 1000);
    		}
    		
    		temp_node.setX(rand_x);
    		temp_node.setY(rand_y);
    		
    		rand_x = 0;
    		rand_y = 0;
    		
    		temp.addLast(temp_node);
    	}
    	
        /* THIRD */
        x_range = width / 2;
    	y_range = height / 2;
    	
        for(int i = 0; i < div_nodes; i++) {
    		temp_node = (Node)nodes.pop();
    		
    		while(!(rand_x >= 50 && rand_x <= x_range && rand_y >= y_range && rand_y <= height)) {
    			rand_x = (int)(Math.random() * 1000);
    			rand_y = (int)(Math.random() * 1000);
    		}
    		
    		temp_node.setX(rand_x);
    		temp_node.setY(rand_y);
    		
    		rand_x = 0;
    		rand_y = 0;
    		
    		temp.addLast(temp_node);
    	}
    	
        /* FORTH */
        x_range = width / 2;
    	
        for(int i = 0; i < div_nodes; i++) {
    		temp_node = (Node)nodes.pop();
    		
    		while(!(rand_x >= x_range && rand_x <= width && rand_y >= y_range && rand_y <= height)) {
    			rand_x = (int)(Math.random() * 1000);
    			rand_y = (int)(Math.random() * 1000);
    		}
    		
    		temp_node.setX(rand_x);
    		temp_node.setY(rand_y);
    		
    		rand_x = 0;
    		rand_y = 0;
    		
    		temp.addLast(temp_node);
    	}
        
        if(nodes.size() != 0) {
        	for(int i = 0; i < nodes.size(); i++) {
        		temp_node = (Node)nodes.pop();
        		while(!(rand_x >= 50 && rand_x <= width && rand_y >= 50 && rand_y <= height)) {
        			rand_x = (int)(Math.random() * 1000);
        			rand_y = (int)(Math.random() * 1000);
        		}
        		
        		temp_node.setX(rand_x);
        		temp_node.setY(rand_y);
        		
        		rand_x = 0;
        		rand_y = 0;
        		
        		temp.addLast(temp_node);
        	}
        }
        
        nodes = temp;
        System.out.println("DONE");
    }
	
	public void setMode(int mode) {
		
		this.mode = mode;
	}
	
	public int distance(int X_S, int Y_S, int X_D, int Y_D) {
		
		return (int)Math.sqrt(((X_S - X_D) * (X_S - X_D)) + ((Y_S - Y_D) * (Y_S - Y_D)));
	}
	
	public void reset(Graph_Panel p) {
		
		this.p = p;
		p.reset();
		
		edges = null;
		nodes = null;
	}
}

